Introduction
This is an notebook to show the process I usually take to analyze
data in order to solve some questions related to database analysis.
The source of the database is from Kaggle.
The data is a relational database of sqlite and it have 7 tables with
information of 8 years of football seasons (2008-2015) of 11 European
countries.
This code was made in Rstudio and has some querys from
SLQ integrated in order to show my abilities in both
languages. This works because the source of data is an sql file but it
can be imported to Rstudio.
For this project, I want to answer this questions related to the
data: 1. ¿What was the average age of football players in Europe in 2008
and 2016? 2. ¿Who was the best player in Europe according to the overall
ranking? 3. What team score the most goals in Europe between 2008 and
2016? 4. ¿What was the team that had the most points in Europe and what
teams were the best home or away?
Import data
The first step I take in the analysis process is to get to know the
database.
Let´s start charging the packages we need:
After downloading the data, we need to import it to Rstudio. First,
we need to establish a connection between Rstudio and the SQL
database
bd_football <- dbConnect(SQLite(), dbname="database.sqlite")
The package RSQLite includes different functions to
manipulate SQL with querys in Rstudio. We see the names of the tables
that the database includes
print(bd_names)
[1] "Country" "League" "Match" "Player"
[5] "Player_Attributes" "Team" "Team_Attributes" "sqlite_sequence"
With that code we can see all names in the database, so we can select
the table Player and see more of it.We need to use SQL code to
get the table to Rstudio:
players <- #save the name of the table
as_tibble( #Convert to tibble
dbGetQuery(bd_football,
"Select* from Player")) %>%
clean_names()
players #Print the tibble players
Now, we can start to answer the above questions
1.What was the average age of football players in Europe in 2008 and
2016?
We saw that the table Players had the information needed to answe
this first question. There are to ways we can do it. The first is with
SQL:
as_tibble(dbGetQuery(bd_football,
"Select
avg(strftime('%Y', '2008-12-12 00:00:00') -
strftime('%Y', birthday)) as average_age_2008,
avg(strftime('%Y', '2016-12-12 00:00:00') -
strftime('%Y', birthday)) as average_age_2016
From Player"))
That SQL query helped us to extract the year of the date of birth of
each player and then we rest it to 2008 and 2016 so we could get the
average. However, we can also do this in R:
players %>%
select(birthday) %>%
mutate(birthday = as_date(birthday),
year_of_birth = year(birthday),
age_2008 = 2008 - year_of_birth,
age_2016 = 2016 - year_of_birth) %>%
summarise(avg_age_2008 = mean(age_2008),
avg_age_2016 = mean(age_2016))
So, we have the average of age but we can do a little bit more to
explore the data. For example, lets include a graph of the relation of
height and weight of the players.
players %>%
mutate(birthday = as_date(birthday),
year_of_birth = year(birthday),
age_2008 = 2008 - year_of_birth) %>% #Obtain the age of the players in 2008
mutate(group_age = if_else(age_2008 <21, #New variable to get the group of age
"Less than 21",
if_else(age_2008 <26,
"Less than 26",
if_else(age_2008 <26,
"Less than 26",
if_else(age_2008 <30,
"Less than 30",
"More than 30")))
)) %>%
ggplot(aes(weight, height, col = group_age))+ #Start the graph
geom_jitter(width = 1.5)+ #Add some noise to the points
guides(colour = guide_legend(override.aes = list(size=3)))+ #Make the point in the legend bigger
labs(title = "Relation Height and Weight",
subtitle = "European Football Players in 2008",
x = "W (LB)",
y = "H (CM)",
col = "Age Group")+
scale_color_manual(values = c("#440154", #Make the color scale
"#3b528b",
"#21918c",
"#fde725"))+
scale_x_continuous(breaks = c(120, 150, 175,200, 220, 240))+ #Make the breaks
theme(plot.title = element_text(hjust = .5),
plot.subtitle = element_text(hjust = .5),
legend.background = element_blank(),
legend.key = element_blank()) #Adjust some elements

2. Who was the best player in Europe according to the overall
ranking?
First, lets get the data with SQL. We can get data from the table
Player such as the height, weight and name. With the table
Player_Attributes, we can find the overall rating in every season so we
can do an average. With this simple SQL code we get the data from both
tables and then create a new variable with Tidyverse.
(rating_height <- as_tibble(dbGetQuery(bd_football,
"Select b.player_name,
b.height,
b.weight,
sum(a.overall_rating) as overall_rating,
avg(overall_rating) as average_rating
from Player_Attributes as a
Left Join Player as b
On b.player_api_id = a.player_api_id
Group by a.player_api_id
Order by average_rating desc")) %>%
mutate(weight = weight * .454,
weight_group = if_else(weight<=60, "Less than 60kg",
if_else(weight<=70, "Less than 70kg",
if_else(weight <= 80, "Less than 75kg",
"More than 80kg")))))
We found that Lionel Messi was the best ranked player with a overall
rating in Europe between 2008 and 2016. But we can use the tibble
created above to graph the distribution of rating and height to see the
relation.
rating_height %>%
ggplot(aes(height, average_rating))+
geom_jitter(aes(col = weight_group),
size = 2.5,
width = 1.5,
height = 1.5)+
geom_text(data = rating_height %>%
filter(average_rating == max(rating_height$average_rating)|
average_rating == max(rating_height$average_rating[rating_height$average_rating!=max(rating_height$average_rating)])),
aes(label = player_name),
check_overlap = T,
nudge_x = 0.16,
hjust = 0)+
scale_color_manual(values = c("#fde725",
"#21918c",
"#3b528b",
"#440154"))+
guides(colour = guide_legend(override.aes = list(size=4)))+
labs(title = "Average Overall Ranking and Height in European Footballers",
subtitle = "2008 - 2016",
color = "Weight Group",
x = "Height",
y = "Average Overall Rating")+
theme(plot.title = element_text(hjust = .5),
plot.subtitle = element_text(hjust = .5),
legend.background = element_blank(),
legend.key = element_blank())

3.What team score the most goals in Europe between 2008 and
2016?
Lets answer this question with a simple SQL code. We need to use two
tables, the Team and Match so we can have the complete name of the team
and sum the away and home goals of each team so we can get the top
scorers.
as_tibble(dbGetQuery(bd_football,
"SELECT
b.team_long_name as team,
sum(a.home_team_goal)+ sum(a.away_team_goal) as goals
FROM Match as a
Left Join Team as b on b.team_api_id = a.home_team_api_id
Group by b.team_api_id
Order by goals Desc
"))
Lets see the distrution of goals scored by european teams:
To answer the next questions, lets start by creating a new table with
the data we need. Now, we know the information of the tables Match and
Team, so we can get the information of each match played in every season
in all countries. Also, based on the goals that each team scored we can
make the points each one got. If you are not familiar with football,
when a team wins they get 3 points, 1 for a tie and 0 for losing. The
next SQL code creates that table called points:
(points <- as_tibble(dbGetQuery(bd_football,
"SELECT
b.team_long_name as home_team,
a.home_team_goal as home_goals,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 3
When a.home_team_goal < a.away_team_goal Then 0
End) as home_point,
c.team_long_name as away_team,
a.away_team_goal as away_goals,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 0
When a.home_team_goal < a.away_team_goal Then 3
End) as away_point,
a.date,
a.season,
d.name as league_name,
e.name as country
FROM Match as a
Left Join Team as b on b.team_api_id = a.home_team_api_id
Left Join Team as c on c.team_api_id = a.away_team_api_id
Left Join League as d on d.country_id = a.country_id
Left Join Country as e on e.id = a.country_id
")))
4. What was the team that had the most points in Europe and what
teams were the best home and away team?
Let´s see how can we get the information with a more complex SQL
query with subquerys and joins to get the variables we want:
as_tibble(dbGetQuery(bd_football,
"
Select
home.home_team as team,
home.total_home_points as total_home_points,
away.total_away_points as total_away_points
From
(
Select
z.id_home as id_home,
z.home_team,
sum(z.home_point) as total_home_points
From
(Select
b.team_long_name as home_team,
c.team_long_name as away_team,
a.home_team_api_id as id_home,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 3
When a.home_team_goal < a.away_team_goal Then 0
End) as home_point,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 0
When a.home_team_goal < a.away_team_goal Then 3
End) as away_point,
a.away_team_api_id as id_away
FROM Match as a
Left Join Team as b on b.team_api_id = a.home_team_api_id
Left Join Team as c on c.team_api_id = a.away_team_api_id) as z
Group by id_home) as home
Inner Join (
Select
z.id_away as id_away,
z.away_team,
sum(z.away_point) as total_away_points
From
(Select
b.team_long_name as home_team,
c.team_long_name as away_team,
a.home_team_api_id as id_home,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 3
When a.home_team_goal < a.away_team_goal Then 0
End) as home_point,
(Case
When a.home_team_goal = a.away_team_goal Then 1
When a.home_team_goal > a.away_team_goal Then 0
When a.home_team_goal < a.away_team_goal Then 3
End) as away_point,
a.away_team_api_id as id_away
FROM Match as a
Left Join Team as b on b.team_api_id = a.home_team_api_id
Left Join Team as c on c.team_api_id = a.away_team_api_id) as z
Group by id_away) as away
On away.away_team = home.home_team
Order by total_home_points desc"
))
Now, the same process but in Tidyverse and a graph to visualize the
data:
points_home <- points %>%
group_by(home_team) %>%
summarise(sum_home_points = sum(home_point)) %>%
rename(team = "home_team") %>%
arrange(-sum_home_points) %>%
filter(sum_home_points > 330)
points_away <- points %>%
group_by(away_team) %>%
summarise(sum_away_points = sum(away_point)) %>%
rename(team = "away_team") %>%
arrange(-sum_away_points) %>%
filter(sum_away_points >250)
home_away_points <- inner_join(points_home, points_away, by = join_by(team)) %>%
pivot_longer(-team,
names_to = "away_or_home",
values_to = "points") %>%
mutate(away_or_home = if_else(away_or_home == "sum_home_points",
"Home points",
"Away points")) %>%
ggplot(aes(fct_reorder(team, points), points))+
geom_col(aes(fill = away_or_home),
position = position_dodge(width = .9),
col = "black")+
coord_flip()+
scale_y_continuous(expand = c(0, 0),
limits = c(0, 420))+
scale_fill_manual(values = c("#B12A90FF",
"#0D0887FF"))+
labs(title = "Home and Away Points in Europe",
subtitle = "2008-2016",
fill = "",
x = "Team",
y = "Points")+
theme_bw()+
theme(panel.grid = element_line(linetype = 3,
color = "black"),
panel.grid.minor = element_blank(),
axis.text = element_text(size = 12),
legend.spacing.y = unit(.2, "cm"),
legend.title = element_text(hjust = .5),
legend.position = "bottom",
plot.title = element_text(hjust = .5,
face = "bold"),
plot.subtitle = element_text(hjust = .5,
face = "bold"))
ggplotly(home_away_points, width = 1000)
NA
LS0tDQp0aXRsZTogIkZvb3RiYWxsIEFuYWx5c2lzIg0KYXV0aG9yOiAiSGVsaW9zIEdhcmNpYSINCmRhdGU6ICJNYXkgMjAyMyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIEludHJvZHVjdGlvbg0KDQpUaGlzIGlzIGFuIG5vdGVib29rIHRvIHNob3cgdGhlIHByb2Nlc3MgSSB1c3VhbGx5IHRha2UgdG8gYW5hbHl6ZSBkYXRhIGluIG9yZGVyIHRvIHNvbHZlIHNvbWUgcXVlc3Rpb25zIHJlbGF0ZWQgdG8gZGF0YWJhc2UgYW5hbHlzaXMuDQoNClRoZSBzb3VyY2Ugb2YgdGhlIGRhdGFiYXNlIGlzIGZyb20gW0thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9odWdvbWF0aGllbi9zb2NjZXIpLiBUaGUgZGF0YSBpcyBhIHJlbGF0aW9uYWwgZGF0YWJhc2Ugb2Ygc3FsaXRlIGFuZCBpdCBoYXZlIDcgdGFibGVzIHdpdGggaW5mb3JtYXRpb24gb2YgOCB5ZWFycyBvZiBmb290YmFsbCBzZWFzb25zICgyMDA4LTIwMTUpIG9mIDExIEV1cm9wZWFuIGNvdW50cmllcy4NCg0KVGhpcyBjb2RlIHdhcyBtYWRlIGluICpSc3R1ZGlvKiBhbmQgaGFzIHNvbWUgcXVlcnlzIGZyb20gKipTTFEqKiBpbnRlZ3JhdGVkIGluIG9yZGVyIHRvIHNob3cgbXkgYWJpbGl0aWVzIGluIGJvdGggbGFuZ3VhZ2VzLiBUaGlzIHdvcmtzIGJlY2F1c2UgdGhlIHNvdXJjZSBvZiBkYXRhIGlzIGFuIHNxbCBmaWxlIGJ1dCBpdCBjYW4gYmUgaW1wb3J0ZWQgdG8gUnN0dWRpby4NCg0KRm9yIHRoaXMgcHJvamVjdCwgSSB3YW50IHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9ucyByZWxhdGVkIHRvIHRoZSBkYXRhOiAxLiDCv1doYXQgd2FzIHRoZSBhdmVyYWdlIGFnZSBvZiBmb290YmFsbCBwbGF5ZXJzIGluIEV1cm9wZSBpbiAyMDA4IGFuZCAyMDE2PyAyLiDCv1dobyB3YXMgdGhlIGJlc3QgcGxheWVyIGluIEV1cm9wZSBhY2NvcmRpbmcgdG8gdGhlIG92ZXJhbGwgcmFua2luZz8gMy4gV2hhdCB0ZWFtIHNjb3JlIHRoZSBtb3N0IGdvYWxzIGluIEV1cm9wZSBiZXR3ZWVuIDIwMDggYW5kIDIwMTY/IDQuIMK/V2hhdCB3YXMgdGhlIHRlYW0gdGhhdCBoYWQgdGhlIG1vc3QgcG9pbnRzIGluIEV1cm9wZSBhbmQgd2hhdCB0ZWFtcyB3ZXJlIHRoZSBiZXN0IGhvbWUgb3IgYXdheT8NCg0KIyMgSW1wb3J0IGRhdGENCg0KVGhlIGZpcnN0IHN0ZXAgSSB0YWtlIGluIHRoZSBhbmFseXNpcyBwcm9jZXNzIGlzIHRvIGdldCB0byBrbm93IHRoZSBkYXRhYmFzZS4NCg0KTGV0wrRzIHN0YXJ0IGNoYXJnaW5nIHRoZSBwYWNrYWdlcyB3ZSBuZWVkOg0KDQpgYGB7cn0NCmxpYnJhcnkoUlNRTGl0ZSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQpBZnRlciBkb3dubG9hZGluZyB0aGUgZGF0YSwgd2UgbmVlZCB0byBpbXBvcnQgaXQgdG8gUnN0dWRpby4gRmlyc3QsIHdlIG5lZWQgdG8gZXN0YWJsaXNoIGEgY29ubmVjdGlvbiBiZXR3ZWVuIFJzdHVkaW8gYW5kIHRoZSBTUUwgZGF0YWJhc2UNCg0KYGBge3J9DQpiZF9mb290YmFsbCA8LSBkYkNvbm5lY3QoU1FMaXRlKCksIGRibmFtZT0iZGF0YWJhc2Uuc3FsaXRlIikNCmBgYA0KDQpUaGUgcGFja2FnZSAqUlNRTGl0ZSogaW5jbHVkZXMgZGlmZmVyZW50IGZ1bmN0aW9ucyB0byBtYW5pcHVsYXRlIFNRTCB3aXRoIHF1ZXJ5cyBpbiBSc3R1ZGlvLiBXZSBzZWUgdGhlIG5hbWVzIG9mIHRoZSB0YWJsZXMgdGhhdCB0aGUgZGF0YWJhc2UgaW5jbHVkZXMNCg0KYGBge3IgZWNobz1UUlVFfQ0KYmRfbmFtZXMgPC0gZGJMaXN0VGFibGVzKGJkX2Zvb3RiYWxsKQ0KDQpwcmludChiZF9uYW1lcykNCmBgYA0KDQpXaXRoIHRoYXQgY29kZSB3ZSBjYW4gc2VlIGFsbCBuYW1lcyBpbiB0aGUgZGF0YWJhc2UsIHNvIHdlIGNhbiBzZWxlY3QgdGhlIHRhYmxlICpQbGF5ZXIqIGFuZCBzZWUgbW9yZSBvZiBpdC5XZSBuZWVkIHRvIHVzZSBTUUwgY29kZSB0byBnZXQgdGhlIHRhYmxlIHRvIFJzdHVkaW86DQoNCmBgYHtyIGVjaG89VFJVRX0NCnBsYXllcnMgPC0gI3NhdmUgdGhlIG5hbWUgb2YgdGhlIHRhYmxlDQogIGFzX3RpYmJsZSggI0NvbnZlcnQgdG8gdGliYmxlDQogICAgZGJHZXRRdWVyeShiZF9mb290YmFsbCwgDQogICAgICAgICAgICAgICAiU2VsZWN0KiBmcm9tIFBsYXllciIpKSAlPiUgDQogIGNsZWFuX25hbWVzKCkNCg0KcGxheWVycyAjUHJpbnQgdGhlIHRpYmJsZSBwbGF5ZXJzDQpgYGANCg0KTm93LCB3ZSBjYW4gc3RhcnQgdG8gYW5zd2VyIHRoZSBhYm92ZSBxdWVzdGlvbnMNCg0KIyAxLldoYXQgd2FzIHRoZSBhdmVyYWdlIGFnZSBvZiBmb290YmFsbCBwbGF5ZXJzIGluIEV1cm9wZSBpbiAyMDA4IGFuZCAyMDE2Pw0KDQpXZSBzYXcgdGhhdCB0aGUgdGFibGUgUGxheWVycyBoYWQgdGhlIGluZm9ybWF0aW9uIG5lZWRlZCB0byBhbnN3ZSB0aGlzIGZpcnN0IHF1ZXN0aW9uLiBUaGVyZSBhcmUgdG8gd2F5cyB3ZSBjYW4gZG8gaXQuIFRoZSBmaXJzdCBpcyB3aXRoIFNRTDoNCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsIA0KICAgICAgICAgICAgICAgICAgICAgIlNlbGVjdCANCiAgICAgICAgICAgICAgICAgICAgYXZnKHN0cmZ0aW1lKCclWScsICcyMDA4LTEyLTEyIDAwOjAwOjAwJykgLSANCiAgICAgICAgICAgICAgICAgICAgIHN0cmZ0aW1lKCclWScsIGJpcnRoZGF5KSkgIGFzIGF2ZXJhZ2VfYWdlXzIwMDgsDQogICAgICAgICAgICAgICAgICAgICBhdmcoc3RyZnRpbWUoJyVZJywgJzIwMTYtMTItMTIgMDA6MDA6MDAnKSAtIA0KICAgICAgICAgICAgICAgICAgICAgc3RyZnRpbWUoJyVZJywgYmlydGhkYXkpKSBhcyBhdmVyYWdlX2FnZV8yMDE2DQogICAgICAgICAgICAgICAgICAgICBGcm9tIFBsYXllciIpKQ0KYGBgDQoNClRoYXQgU1FMIHF1ZXJ5IGhlbHBlZCB1cyB0byBleHRyYWN0IHRoZSB5ZWFyIG9mIHRoZSBkYXRlIG9mIGJpcnRoIG9mIGVhY2ggcGxheWVyIGFuZCB0aGVuIHdlIHJlc3QgaXQgdG8gMjAwOCBhbmQgMjAxNiBzbyB3ZSBjb3VsZCBnZXQgdGhlIGF2ZXJhZ2UuIEhvd2V2ZXIsIHdlIGNhbiBhbHNvIGRvIHRoaXMgaW4gUjoNCg0KYGBge3IgZWNobz1UUlVFfQ0KcGxheWVycyAlPiUgDQogIHNlbGVjdChiaXJ0aGRheSkgJT4lIA0KICBtdXRhdGUoYmlydGhkYXkgPSBhc19kYXRlKGJpcnRoZGF5KSwNCiAgICAgICAgIHllYXJfb2ZfYmlydGggPSB5ZWFyKGJpcnRoZGF5KSwNCiAgICAgICAgIGFnZV8yMDA4ID0gMjAwOCAtIHllYXJfb2ZfYmlydGgsDQogICAgICAgICBhZ2VfMjAxNiA9IDIwMTYgLSB5ZWFyX29mX2JpcnRoKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfYWdlXzIwMDggPSBtZWFuKGFnZV8yMDA4KSwNCiAgICAgICAgICAgIGF2Z19hZ2VfMjAxNiA9IG1lYW4oYWdlXzIwMTYpKQ0KYGBgDQoNClNvLCB3ZSBoYXZlIHRoZSBhdmVyYWdlIG9mIGFnZSBidXQgd2UgY2FuIGRvIGEgbGl0dGxlIGJpdCBtb3JlIHRvIGV4cGxvcmUgdGhlIGRhdGEuIEZvciBleGFtcGxlLCBsZXRzIGluY2x1ZGUgYSBncmFwaCBvZiB0aGUgcmVsYXRpb24gb2YgaGVpZ2h0IGFuZCB3ZWlnaHQgb2YgdGhlIHBsYXllcnMuDQoNCmBgYHtyIGVjaG89VFJVRX0NCg0KcGxheWVycyAlPiUgDQogIG11dGF0ZShiaXJ0aGRheSA9IGFzX2RhdGUoYmlydGhkYXkpLA0KICAgICAgICAgeWVhcl9vZl9iaXJ0aCA9IHllYXIoYmlydGhkYXkpLA0KICAgICAgICAgYWdlXzIwMDggPSAyMDA4IC0geWVhcl9vZl9iaXJ0aCkgJT4lICNPYnRhaW4gdGhlIGFnZSBvZiB0aGUgcGxheWVycyBpbiAyMDA4DQogIG11dGF0ZShncm91cF9hZ2UgPSBpZl9lbHNlKGFnZV8yMDA4IDwyMSwgI05ldyB2YXJpYWJsZSB0byBnZXQgdGhlIGdyb3VwIG9mIGFnZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVzcyB0aGFuIDIxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShhZ2VfMjAwOCA8MjYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMZXNzIHRoYW4gMjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoYWdlXzIwMDggPDI2LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMZXNzIHRoYW4gMjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShhZ2VfMjAwOCA8MzAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVzcyB0aGFuIDMwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1vcmUgdGhhbiAzMCIpKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpICU+JSANCiAgZ2dwbG90KGFlcyh3ZWlnaHQsIGhlaWdodCwgY29sID0gZ3JvdXBfYWdlKSkrICNTdGFydCB0aGUgZ3JhcGgNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAxLjUpKyAjQWRkIHNvbWUgbm9pc2UgdG8gdGhlIHBvaW50cw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0zKSkpKyAjTWFrZSB0aGUgcG9pbnQgaW4gdGhlIGxlZ2VuZCBiaWdnZXINCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbiBIZWlnaHQgYW5kIFdlaWdodCIsDQogICAgICAgc3VidGl0bGUgPSAiRXVyb3BlYW4gRm9vdGJhbGwgUGxheWVycyBpbiAyMDA4IiwNCiAgICAgICB4ID0gIlcgKExCKSIsDQogICAgICAgeSA9ICJIIChDTSkiLA0KICAgICAgIGNvbCA9ICJBZ2UgR3JvdXAiKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiM0NDAxNTQiLCAjTWFrZSB0aGUgY29sb3Igc2NhbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMzYjUyOGIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMyMTkxOGMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI2ZkZTcyNSIpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMTIwLCAxNTAsIDE3NSwyMDAsIDIyMCwgMjQwKSkrICNNYWtlIHRoZSBicmVha3MNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpICNBZGp1c3Qgc29tZSBlbGVtZW50cw0KDQpgYGANCg0KIyAyLiBXaG8gd2FzIHRoZSBiZXN0IHBsYXllciBpbiBFdXJvcGUgYWNjb3JkaW5nIHRvIHRoZSBvdmVyYWxsIHJhbmtpbmc/DQoNCkZpcnN0LCBsZXRzIGdldCB0aGUgZGF0YSB3aXRoIFNRTC4gV2UgY2FuIGdldCBkYXRhIGZyb20gdGhlIHRhYmxlIFBsYXllciBzdWNoIGFzIHRoZSBoZWlnaHQsIHdlaWdodCBhbmQgbmFtZS4gV2l0aCB0aGUgdGFibGUgUGxheWVyX0F0dHJpYnV0ZXMsIHdlIGNhbiBmaW5kIHRoZSBvdmVyYWxsIHJhdGluZyBpbiBldmVyeSBzZWFzb24gc28gd2UgY2FuIGRvIGFuIGF2ZXJhZ2UuIFdpdGggdGhpcyBzaW1wbGUgU1FMIGNvZGUgd2UgZ2V0IHRoZSBkYXRhIGZyb20gYm90aCB0YWJsZXMgYW5kIHRoZW4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHdpdGggVGlkeXZlcnNlLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQoocmF0aW5nX2hlaWdodCA8LSBhc190aWJibGUoZGJHZXRRdWVyeShiZF9mb290YmFsbCwgDQogICAgICAgICAgICAgICAgICAgICAiU2VsZWN0IGIucGxheWVyX25hbWUsIA0KICAgICAgICAgICAgICAgICAgICAgYi5oZWlnaHQsIA0KICAgICAgICAgICAgICAgICAgICAgYi53ZWlnaHQsDQogICAgICAgICAgICAgICAgICAgICBzdW0oYS5vdmVyYWxsX3JhdGluZykgYXMgb3ZlcmFsbF9yYXRpbmcsIA0KICAgICAgICAgICAgICAgICAgICAgYXZnKG92ZXJhbGxfcmF0aW5nKSBhcyBhdmVyYWdlX3JhdGluZw0KICAgICAgICAgICAgICAgICAgICAgZnJvbSBQbGF5ZXJfQXR0cmlidXRlcyBhcyBhDQogICAgICAgICAgICAgICAgICAgICBMZWZ0IEpvaW4gUGxheWVyIGFzIGINCiAgICAgICAgICAgICAgICAgICAgIE9uIGIucGxheWVyX2FwaV9pZCA9IGEucGxheWVyX2FwaV9pZA0KICAgICAgICAgICAgICAgICAgICAgR3JvdXAgYnkgYS5wbGF5ZXJfYXBpX2lkDQogICAgICAgICAgICAgICAgICAgICBPcmRlciBieSBhdmVyYWdlX3JhdGluZyBkZXNjIikpICU+JSANCiAgbXV0YXRlKHdlaWdodCA9IHdlaWdodCAqIC40NTQsDQogICAgICAgICB3ZWlnaHRfZ3JvdXAgPSBpZl9lbHNlKHdlaWdodDw9NjAsICJMZXNzIHRoYW4gNjBrZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2Uod2VpZ2h0PD03MCwgIkxlc3MgdGhhbiA3MGtnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHdlaWdodCA8PSA4MCwgIkxlc3MgdGhhbiA3NWtnIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNb3JlIHRoYW4gODBrZyIpKSkpKQ0KYGBgDQoNCldlIGZvdW5kIHRoYXQgTGlvbmVsIE1lc3NpIHdhcyB0aGUgYmVzdCByYW5rZWQgcGxheWVyIHdpdGggYSBvdmVyYWxsIHJhdGluZyBpbiBFdXJvcGUgYmV0d2VlbiAyMDA4IGFuZCAyMDE2LiBCdXQgd2UgY2FuIHVzZSB0aGUgdGliYmxlIGNyZWF0ZWQgYWJvdmUgdG8gZ3JhcGggdGhlIGRpc3RyaWJ1dGlvbiBvZiByYXRpbmcgYW5kIGhlaWdodCB0byBzZWUgdGhlIHJlbGF0aW9uLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpyYXRpbmdfaGVpZ2h0ICU+JSANCiAgZ2dwbG90KGFlcyhoZWlnaHQsIGF2ZXJhZ2VfcmF0aW5nKSkrDQogIGdlb21faml0dGVyKGFlcyhjb2wgPSB3ZWlnaHRfZ3JvdXApLA0KICAgICAgICAgICAgICBzaXplID0gMi41LA0KICAgICAgICAgICAgICB3aWR0aCA9IDEuNSwNCiAgICAgICAgICAgICAgaGVpZ2h0ID0gMS41KSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSByYXRpbmdfaGVpZ2h0ICU+JSANCiAgICAgICAgICAgICAgZmlsdGVyKGF2ZXJhZ2VfcmF0aW5nID09IG1heChyYXRpbmdfaGVpZ2h0JGF2ZXJhZ2VfcmF0aW5nKXwNCiAgICAgICAgICAgICAgICAgICAgICAgYXZlcmFnZV9yYXRpbmcgPT0gbWF4KHJhdGluZ19oZWlnaHQkYXZlcmFnZV9yYXRpbmdbcmF0aW5nX2hlaWdodCRhdmVyYWdlX3JhdGluZyE9bWF4KHJhdGluZ19oZWlnaHQkYXZlcmFnZV9yYXRpbmcpXSkpLA0KICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBsYXllcl9uYW1lKSwNCiAgICAgICAgICAgIGNoZWNrX292ZXJsYXAgPSBULCANCiAgICAgICAgICAgIG51ZGdlX3ggPSAwLjE2LA0KICAgICAgICAgICAgaGp1c3QgPSAwKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNmZGU3MjUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzIxOTE4YyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzNiNTI4YiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjNDQwMTU0IikpKw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT00KSkpKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgT3ZlcmFsbCBSYW5raW5nIGFuZCBIZWlnaHQgaW4gRXVyb3BlYW4gRm9vdGJhbGxlcnMiLA0KICAgICAgIHN1YnRpdGxlID0gIjIwMDggLSAyMDE2IiwNCiAgICAgICBjb2xvciA9ICJXZWlnaHQgR3JvdXAiLA0KICAgICAgIHggPSAiSGVpZ2h0IiwNCiAgICAgICB5ID0gIkF2ZXJhZ2UgT3ZlcmFsbCBSYXRpbmciKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KIyAzLldoYXQgdGVhbSBzY29yZSB0aGUgbW9zdCBnb2FscyBpbiBFdXJvcGUgYmV0d2VlbiAyMDA4IGFuZCAyMDE2Pw0KDQpMZXRzIGFuc3dlciB0aGlzIHF1ZXN0aW9uIHdpdGggYSBzaW1wbGUgU1FMIGNvZGUuIFdlIG5lZWQgdG8gdXNlIHR3byB0YWJsZXMsIHRoZSBUZWFtIGFuZCBNYXRjaCBzbyB3ZSBjYW4gaGF2ZSB0aGUgY29tcGxldGUgbmFtZSBvZiB0aGUgdGVhbSBhbmQgc3VtIHRoZSBhd2F5IGFuZCBob21lIGdvYWxzIG9mIGVhY2ggdGVhbSBzbyB3ZSBjYW4gZ2V0IHRoZSB0b3Agc2NvcmVycy4NCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsIA0KICAgICAgICAgICAgICAgICAgICAgIlNFTEVDVA0KICAgICAgICAgICBiLnRlYW1fbG9uZ19uYW1lIGFzIHRlYW0sDQogICAgICAgICAgIHN1bShhLmhvbWVfdGVhbV9nb2FsKSsgc3VtKGEuYXdheV90ZWFtX2dvYWwpIGFzIGdvYWxzDQogICAgICAgICAgIEZST00gTWF0Y2ggYXMgYQ0KICAgICAgICAgICBMZWZ0IEpvaW4gVGVhbSBhcyBiIG9uIGIudGVhbV9hcGlfaWQgPSBhLmhvbWVfdGVhbV9hcGlfaWQgDQogICAgICAgICAgIEdyb3VwIGJ5IGIudGVhbV9hcGlfaWQNCiAgICAgICAgICAgT3JkZXIgYnkgZ29hbHMgRGVzYw0KICAgICAgICAgICAiKSkNCmBgYA0KDQpMZXRzIHNlZSB0aGUgZGlzdHJ1dGlvbiBvZiBnb2FscyBzY29yZWQgYnkgZXVyb3BlYW4gdGVhbXM6DQoNClRvIGFuc3dlciB0aGUgbmV4dCBxdWVzdGlvbnMsIGxldHMgc3RhcnQgYnkgY3JlYXRpbmcgYSBuZXcgdGFibGUgd2l0aCB0aGUgZGF0YSB3ZSBuZWVkLiBOb3csIHdlIGtub3cgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0YWJsZXMgTWF0Y2ggYW5kIFRlYW0sIHNvIHdlIGNhbiBnZXQgdGhlIGluZm9ybWF0aW9uIG9mIGVhY2ggbWF0Y2ggcGxheWVkIGluIGV2ZXJ5IHNlYXNvbiBpbiBhbGwgY291bnRyaWVzLiBBbHNvLCBiYXNlZCBvbiB0aGUgZ29hbHMgdGhhdCBlYWNoIHRlYW0gc2NvcmVkIHdlIGNhbiBtYWtlIHRoZSBwb2ludHMgZWFjaCBvbmUgZ290LiBJZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIGZvb3RiYWxsLCB3aGVuIGEgdGVhbSB3aW5zIHRoZXkgZ2V0IDMgcG9pbnRzLCAxIGZvciBhIHRpZSBhbmQgMCBmb3IgbG9zaW5nLiBUaGUgbmV4dCBTUUwgY29kZSBjcmVhdGVzIHRoYXQgdGFibGUgY2FsbGVkIHBvaW50czoNCg0KYGBge3IgZWNobz1UUlVFfQ0KDQoocG9pbnRzIDwtIGFzX3RpYmJsZShkYkdldFF1ZXJ5KGJkX2Zvb3RiYWxsLCANCiAgICAgICAgICAgICAgICAgICAgICJTRUxFQ1QNCiAgICAgICAgICAgYi50ZWFtX2xvbmdfbmFtZSBhcyBob21lX3RlYW0sDQogICAgICAgICAgIGEuaG9tZV90ZWFtX2dvYWwgYXMgaG9tZV9nb2FscywNCiAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID4gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDANCiAgICAgICAgICAgRW5kKSBhcyBob21lX3BvaW50LA0KICAgICAgICAgICBjLnRlYW1fbG9uZ19uYW1lIGFzIGF3YXlfdGVhbSwNCiAgICAgICAgICAgYS5hd2F5X3RlYW1fZ29hbCBhcyBhd2F5X2dvYWxzLA0KICAgICAgICAgICAoQ2FzZQ0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPSBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMQ0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPiBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPCBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMw0KICAgICAgICAgICBFbmQpIGFzIGF3YXlfcG9pbnQsDQogICAgICAgICAgIGEuZGF0ZSwNCiAgICAgICAgICAgYS5zZWFzb24sDQogICAgICAgICAgIGQubmFtZSBhcyBsZWFndWVfbmFtZSwNCiAgICAgICAgICAgZS5uYW1lIGFzIGNvdW50cnkNCiAgICAgICAgICAgRlJPTSBNYXRjaCBhcyBhDQogICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGIgb24gYi50ZWFtX2FwaV9pZCA9IGEuaG9tZV90ZWFtX2FwaV9pZCANCiAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYyBvbiBjLnRlYW1fYXBpX2lkID0gYS5hd2F5X3RlYW1fYXBpX2lkDQogICAgICAgICAgIExlZnQgSm9pbiBMZWFndWUgYXMgZCBvbiBkLmNvdW50cnlfaWQgPSBhLmNvdW50cnlfaWQNCiAgICAgICAgICAgTGVmdCBKb2luIENvdW50cnkgYXMgZSBvbiBlLmlkID0gYS5jb3VudHJ5X2lkDQogICAgICAgICAgICIpKSkgDQpgYGANCg0KIyA0LiBXaGF0IHdhcyB0aGUgdGVhbSB0aGF0IGhhZCB0aGUgbW9zdCBwb2ludHMgaW4gRXVyb3BlIGFuZCB3aGF0IHRlYW1zIHdlcmUgdGhlIGJlc3QgaG9tZSBhbmQgYXdheSB0ZWFtPw0KDQpMZXTCtHMgc2VlIGhvdyBjYW4gd2UgZ2V0IHRoZSBpbmZvcm1hdGlvbiB3aXRoIGEgbW9yZSBjb21wbGV4IFNRTCBxdWVyeSB3aXRoIHN1YnF1ZXJ5cyBhbmQgam9pbnMgdG8gZ2V0IHRoZSB2YXJpYWJsZXMgd2Ugd2FudDoNCg0KYGBge3IgZWNobz1UUlVFfQ0KYXNfdGliYmxlKGRiR2V0UXVlcnkoYmRfZm9vdGJhbGwsDQogICAgICAgICAgICAgICAgICAgICAiDQogICAgICAgICAgICAgIFNlbGVjdA0KICAgICAgICAgICAgICAgIGhvbWUuaG9tZV90ZWFtIGFzIHRlYW0sDQogICAgICAgICAgICAgICAgaG9tZS50b3RhbF9ob21lX3BvaW50cyBhcyB0b3RhbF9ob21lX3BvaW50cywNCiAgICAgICAgICAgICAgICBhd2F5LnRvdGFsX2F3YXlfcG9pbnRzIGFzIHRvdGFsX2F3YXlfcG9pbnRzDQogICAgICAgICAgICAgIEZyb20NCiAgICAgICAgICAgICAgICAgICAgICgNCiAgICAgICAgICAgICAgICAgICAgIFNlbGVjdA0KICAgICAgICAgICAgICAgICAgICAgei5pZF9ob21lIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICB6LmhvbWVfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgIHN1bSh6LmhvbWVfcG9pbnQpIGFzIHRvdGFsX2hvbWVfcG9pbnRzDQogICAgICAgICAgICAgICAgICAgICBGcm9tDQogICAgICAgICAgICAgICAgICAgICAoU2VsZWN0DQogICAgICAgICAgICAgICAgICAgICBiLnRlYW1fbG9uZ19uYW1lIGFzIGhvbWVfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgIGMudGVhbV9sb25nX25hbWUgYXMgYXdheV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgYS5ob21lX3RlYW1fYXBpX2lkIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICAoQ2FzZQ0KICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA+IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAzDQogICAgICAgICAgICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPCBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICAgICAgICAgICAgRW5kKSBhcyBob21lX3BvaW50LA0KICAgICAgICAgICAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA9IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAxDQogICAgICAgICAgICAgICAgICAgICBXaGVuIGEuaG9tZV90ZWFtX2dvYWwgPiBhLmF3YXlfdGVhbV9nb2FsIFRoZW4gMA0KICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgICAgICAgICAgIEVuZCkgYXMgYXdheV9wb2ludCwNCiAgICAgICAgICAgICAgICAgICAgIGEuYXdheV90ZWFtX2FwaV9pZCBhcyBpZF9hd2F5DQogICAgICAgICAgICAgICAgICAgICBGUk9NIE1hdGNoIGFzIGENCiAgICAgICAgICAgICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGIgb24gYi50ZWFtX2FwaV9pZCA9IGEuaG9tZV90ZWFtX2FwaV9pZA0KICAgICAgICAgICAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYyBvbiBjLnRlYW1fYXBpX2lkID0gYS5hd2F5X3RlYW1fYXBpX2lkKSBhcyB6DQogICAgICAgICAgICAgICAgICAgICBHcm91cCBieSBpZF9ob21lKSBhcyBob21lDQogICAgICAgICAgICAgIElubmVyIEpvaW4gKA0KICAgICAgICAgICAgICAgICAgICAgICAgICBTZWxlY3QNCiAgICAgICAgICAgICAgICAgICAgICAgICAgei5pZF9hd2F5IGFzIGlkX2F3YXksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHouYXdheV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oei5hd2F5X3BvaW50KSBhcyB0b3RhbF9hd2F5X3BvaW50cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRnJvbQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAoU2VsZWN0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGIudGVhbV9sb25nX25hbWUgYXMgaG9tZV90ZWFtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjLnRlYW1fbG9uZ19uYW1lIGFzIGF3YXlfdGVhbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYS5ob21lX3RlYW1fYXBpX2lkIGFzIGlkX2hvbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIChDYXNlDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA9IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAxDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA+IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAzDQogICAgICAgICAgICAgICAgICAgICAgICAgIFdoZW4gYS5ob21lX3RlYW1fZ29hbCA8IGEuYXdheV90ZWFtX2dvYWwgVGhlbiAwDQogICAgICAgICAgICAgICAgICAgICAgICAgIEVuZCkgYXMgaG9tZV9wb2ludCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgKENhc2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID0gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsID4gYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDANCiAgICAgICAgICAgICAgICAgICAgICAgICAgV2hlbiBhLmhvbWVfdGVhbV9nb2FsIDwgYS5hd2F5X3RlYW1fZ29hbCBUaGVuIDMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgRW5kKSBhcyBhd2F5X3BvaW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhLmF3YXlfdGVhbV9hcGlfaWQgYXMgaWRfYXdheQ0KICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIE1hdGNoIGFzIGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgTGVmdCBKb2luIFRlYW0gYXMgYiBvbiBiLnRlYW1fYXBpX2lkID0gYS5ob21lX3RlYW1fYXBpX2lkDQogICAgICAgICAgICAgICAgICAgICAgICAgIExlZnQgSm9pbiBUZWFtIGFzIGMgb24gYy50ZWFtX2FwaV9pZCA9IGEuYXdheV90ZWFtX2FwaV9pZCkgYXMgeg0KICAgICAgICAgICAgICAgICAgICAgICAgICBHcm91cCBieSBpZF9hd2F5KSBhcyBhd2F5DQogICAgICAgICAgICAgIE9uIGF3YXkuYXdheV90ZWFtID0gaG9tZS5ob21lX3RlYW0NCiAgICAgICAgICAgICAgT3JkZXIgYnkgdG90YWxfaG9tZV9wb2ludHMgZGVzYyINCiAgDQopKQ0KYGBgDQoNCk5vdywgdGhlIHNhbWUgcHJvY2VzcyBidXQgaW4gVGlkeXZlcnNlIGFuZCBhIGdyYXBoIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YToNCg0KYGBge3IgZWNobz1UUlVFfQ0KcG9pbnRzX2hvbWUgPC0gcG9pbnRzICU+JSANCiAgZ3JvdXBfYnkoaG9tZV90ZWFtKSAlPiUgDQogIHN1bW1hcmlzZShzdW1faG9tZV9wb2ludHMgPSBzdW0oaG9tZV9wb2ludCkpICU+JSANCiAgcmVuYW1lKHRlYW0gPSAiaG9tZV90ZWFtIikgJT4lIA0KICBhcnJhbmdlKC1zdW1faG9tZV9wb2ludHMpICU+JSANCiAgZmlsdGVyKHN1bV9ob21lX3BvaW50cyA+IDMzMCkNCg0KcG9pbnRzX2F3YXkgPC0gcG9pbnRzICU+JSANCiAgZ3JvdXBfYnkoYXdheV90ZWFtKSAlPiUgDQogIHN1bW1hcmlzZShzdW1fYXdheV9wb2ludHMgPSBzdW0oYXdheV9wb2ludCkpICU+JSANCiAgcmVuYW1lKHRlYW0gPSAiYXdheV90ZWFtIikgJT4lIA0KICBhcnJhbmdlKC1zdW1fYXdheV9wb2ludHMpICU+JSANCiAgZmlsdGVyKHN1bV9hd2F5X3BvaW50cyA+MjUwKQ0KDQoNCmhvbWVfYXdheV9wb2ludHMgPC0gaW5uZXJfam9pbihwb2ludHNfaG9tZSwgcG9pbnRzX2F3YXksIGJ5ID0gam9pbl9ieSh0ZWFtKSkgJT4lIA0KICBwaXZvdF9sb25nZXIoLXRlYW0sIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiYXdheV9vcl9ob21lIiwNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwb2ludHMiKSAlPiUNCiAgbXV0YXRlKGF3YXlfb3JfaG9tZSA9IGlmX2Vsc2UoYXdheV9vcl9ob21lID09ICJzdW1faG9tZV9wb2ludHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkhvbWUgcG9pbnRzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJBd2F5IHBvaW50cyIpKSAlPiUgDQogIGdncGxvdChhZXMoZmN0X3Jlb3JkZXIodGVhbSwgcG9pbnRzKSwgcG9pbnRzKSkrDQogIGdlb21fY29sKGFlcyhmaWxsID0gYXdheV9vcl9ob21lKSwgDQogICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAuOSksDQogICAgICAgICAgIGNvbCA9ICJibGFjayIpKw0KICBjb29yZF9mbGlwKCkrDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCA0MjApKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0IxMkE5MEZGIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMEQwODg3RkYiKSkrDQogIGxhYnModGl0bGUgPSAiSG9tZSBhbmQgQXdheSBQb2ludHMgaW4gRXVyb3BlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDA4LTIwMTYiLA0KICAgICAgIGZpbGwgPSAiIiwNCiAgICAgICB4ID0gIlRlYW0iLCANCiAgICAgICB5ID0gIlBvaW50cyIpKw0KICB0aGVtZV9idygpKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgbGVnZW5kLnNwYWNpbmcueSA9IHVuaXQoLjIsICJjbSIpLA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSkNCg0KZ2dwbG90bHkoaG9tZV9hd2F5X3BvaW50cywgd2lkdGggPSAxMDAwKQ0KDQpgYGANCg==